using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Data;
using System.Web;
using System.Xml;
using System.Xml.XPath;

using Actions;
using Actions.Databases;
using Actions.Connectors;
using Actions.Security;
using Actions.Usecases;

namespace Actions.Security
{
	public class SecurityModel
	{
		private string _databaseId = "";		
		private string _regExpType = "^(user|role)$";		
		private string _regExpId = "^[a-zA-Z0-9]{5,64}$";		
		
		public string regType 
		{
			get {
				return _regExpType;
			}
		} 
		public string regId 
		{
			get {
				return _regExpId;
			}
		} 
		
				
		public SecurityModel()
		{
			_databaseId = Manager.i.config.GetValueOf("//Configuration/SecurityAgent/DatabaseID");		
		}
		
		public string showObject(string type, string id)
		{
			string s = "";
			
			switch(type) {
				case("user") : {
					s += Manager.i.db(_databaseId).
						query("SELECT login, name, mail, enable FROM _users ORDER BY name").
						serializeAsList("users", "user");					
					break;
				}		
				case("role") : {
					s += Manager.i.db(_databaseId).
						query("SELECT * FROM _roles ORDER BY title").
						serializeAsList("roles", "role");
					break;
				}		
			}
			return s;		
		}

		public string showObjects()
		{
			string s = "<security><objects>";
			
			s += showObjectsByType("user");
			s += showObjectsByType("role");						
			s += "</objects></security>";
			
			return s;
		}
		
		public string showObjectsByType(string type)
		{
			string s = "";
			
			switch(type) {
				case("user") : {
					s += Manager.i.db(_databaseId).
						query("SELECT login, name, mail, enable FROM _users ORDER BY name").
						serializeAsList("users", "user");					
					break;
				}		
				case("role") : {
					s += Manager.i.db(_databaseId).
						query("SELECT * FROM _roles ORDER BY title").
						serializeAsList("roles", "role");
					break;
				}		
				case("usecase") : {
					s += Manager.i.db(_databaseId).
						query("SELECT * FROM _usecases ORDER BY title").
						serializeAsList("usecases", "usecase");
					break;
				}		
			}
			return s;		
		}

		public string showAddForm(string type)
		{
			return showFormItem(type, "Add", "");
		}
				
		public string showForm(string type, string id)
		{
			string s = "<object type='" + type + "' id='" + id + "'>";
			
			switch(type) {
				case("user") : {
					QueryResult qr = Manager.i.db(_databaseId).
						query("SELECT * FROM _users WHERE login='" + id + "'");						
					s += qr.serializeAsList("value", "item");
					
					string[] r = qr.got(0, "roles").Split(',');
        			s += "<roles_list>";
        			foreach (string ri in r) {
         				s += "<item>" + ri + "</item>";         					
					}											
        			s += "</roles_list>";
					break;
				}		
				case("role") : {
					QueryResult qr = Manager.i.db(_databaseId).
						query("SELECT * FROM _roles WHERE id='" + id + "'");						
					s += qr.serializeAsList("value", "item");
					
					string[] uc = qr.got(0, "usecases").Split(',');
        			s += "<usecases_list>";
        			foreach (string uci in uc) {
         				s += "<item>" + uci + "</item>";         					
					}											
        			s += "</usecases_list>";
					break;
				}		
			}
			s += "</object>";
						
			return showFormItem(type, "Commit", s);
		}
		
		public void add(string type, ref IConnector c)
		{
			switch(type) {
				case("user") : {
					string login = c.getInputParam("f_login");
					string secret = c.getInputParam("f_secret");
					
					if(login != null && login.Length > 0 && Regex.IsMatch(login, _regExpId) &&
						secret != null && secret.Length > 0 && Regex.IsMatch(secret, _regExpId)) {
						string name = Manager.i.db(_databaseId).esc(c.getInputParam("f_name"));
						string mail = Manager.i.db(_databaseId).esc(c.getInputParam("f_mail"));
						string enable = (c.getInputParam("f_enable") == "on") ? "1" : "0";
						string dusecase = Manager.i.db(_databaseId).esc(c.getInputParam("f_dusecase"));
						string roles = Manager.i.db(_databaseId).esc(c.getInputParam("f_roles"));
					
						Manager.i.db(_databaseId).vQuery("INSERT INTO _users SET login='" + 
							login + "', secret=MD5('" + secret + "'), name='" + 
							name + "', mail='" + mail + "', enable='" + 
							enable + "', dusecase='" + dusecase + "', roles='" + 
							roles + "'");
					}
						
					break;
				}		
				case("role") : {
					string id = c.getInputParam("f_id");
					
					if(id != null && id.Length > 0 && Regex.IsMatch(id, _regExpId)) {
						string title  = Manager.i.db(_databaseId).esc(c.getInputParam("f_title"));
						string enable = (c.getInputParam("f_enable") == "on") ? "1" : "0";
						string usecases = Manager.i.db(_databaseId).esc(c.getInputParam("f_usecases"));
				
						Manager.i.db(_databaseId).vQuery("INSERT INTO _roles SET id='" + 
							id + "', title='" + title + "', enable='" + 
							enable + "', usecases='" + usecases + "'");
					}
							
					break;
				}		
			}			
		}
		
		public void commit(string type, string id, ref IConnector c)
		{
			if(type != "role") {
				delete(type, id, "");
				add(type, ref c);
			} else {
				// We need special action for n-to-m links between roles an users
				// If role's id will be chaged we must update linked users				
				// This point of potential unconsistentnosty :) cause role adding may be rejected 
				
				string nid = c.getInputParam("f_id");
				if(nid != null && nid.Length > 0 && Regex.IsMatch(nid, _regExpId) 
					&& nid != id) {									
					delete(type, id, nid);
				} else {
					// dirty an dark hack
					Manager.i.db(_databaseId).vQuery("DELETE FROM _roles WHERE id='" + id + "'");										
				}				
				add(type, ref c);  
			}
		}
		
		public void delete(string type, string id, string replaceId)
		{
			switch(type) {
				case("user") : {
					Manager.i.db(_databaseId).vQuery("DELETE FROM _users WHERE login='" + id + "'");					
					break;
				}		
				case("role") : {
					Manager.i.db(_databaseId).vQuery("DELETE FROM _roles WHERE id='" + id + "'");					
									
					QueryResult qr = Manager.i.db(_databaseId).
						query("SELECT login, roles FROM _users WHERE roles LIKE '%" + id + "%'");						
					for(int i = 0; i < qr.res.Count; i++) {
						string[] r = qr.got(i, "roles").Split(',');
        				ArrayList nr = new ArrayList();
        				
        				if(replaceId.Length < 1) {		
        					foreach(string ri in r) if(ri != id) nr.Add(ri);        					         					
         				} else {
        					foreach(string ri in r) {
        						if(ri != id) nr.Add(ri);
        						else nr.Add(replaceId);
        					}        					         					
         				} 											
        				
        				string ru = String.Join(",", (string[]) nr.ToArray(typeof(string)));
        				Manager.i.db(_databaseId).vQuery("UPDATE _users SET roles='" +
        					ru + "' WHERE login='" + qr.got(i, "login") + "'");				
					}
					
					break;
				}		
			}
		}
		
		public string checkId(string type, ref IConnector c)
		{
			string s = "false";			
			string id = c.getInputParam("id");
			
			if(id != null && id.Length > 0 && Regex.IsMatch(id, _regExpId)) {						
				switch(type) {
					case("user") : {
						QueryResult qr = Manager.i.db(_databaseId).
							query("SELECT login FROM _users WHERE login='" + id + "'");
					
						if(qr.res.Count == 0) s = "true";	
						break;
					}		
					case("role") : {
						QueryResult qr = Manager.i.db(_databaseId).
							query("SELECT id FROM _roles WHERE id='" + id + "'");
					
						if(qr.res.Count == 0) s = "true";	
						break;
					}		
				}
			}
			return  "<result>"  + s + "</result>";
		}

		public string updateUsecaseName(ref IConnector c)
		{
			string usecase = c.getInputParam("uc");
			string title = c.getInputParam("title");
			
			if(usecase != null && usecase.Length > 0 && Regex.IsMatch(usecase, _regExpId)
				&& title != null && title.Length > 0 ) {
				Transaction t = new Transaction();
				t.addQuery("DELETE FROM _usecases WHERE usecase='" + usecase + "'");
				t.addQuery("INSERT INTO _usecases SET usecase='" + usecase + 
						"', title='" + Manager.i.db(_databaseId).esc(title) + "'");
						
				Manager.i.db(_databaseId).vQuery(t);
			}	
			return  "<result>'"  + title + "'</result>";
		}
		
		private string showFormItem(string type, string action, string obj)
		{
			string s = "";

			s += "<" + type + "form action='" + action + "'>";
			s += showObjectsByType("role");								
			s += showObjectsByType("usecase");													
			s += Manager.i.up.getUsecasesListXML();
			s += obj;
			s += "</" + type + "form>";		   

			return s;
		}
		
	}
}